import { createWriteStream, existsSync, mkdir, mkdirSync, rename, stat } from 'fs'
import { platform, arch } from 'os'
import { dirname, join } from 'path'
import { spawn } from 'child_process'
import yauzl from 'yauzl'
import url from './url'
import Download from './Download'
import config from './config'
import { Transform } from 'stream'
import { Name } from './interface'

/**
 * 解压zip
 * @param output 
 * @param input 
 */
export default function Install(output: string, input: string, name: Name) {
    return new Promise<boolean>((res) => {
        if (!existsSync(input)) {
            console.log('没有找到文件')
            res(false)
        }
        let d = ''
        yauzl.open(input, { lazyEntries: true }, function (err, zipfile) {
            if (err) throw err;

            zipfile?.readEntry();
            zipfile?.on('close', () => {
                if (d) {
                    const oldname = getfname(d)
                    rename(join(output, oldname), join(output, name), function (err) {
                        if (err) {
                            res(false)
                        } else {
                            res(true)
                        }
                    })
                }
                res(true)
            })
            zipfile?.on("entry", function (entry) {

                if (/\/$/.test(entry.fileName)) {
                    if (!d) d = entry.fileName
                    // Directory file names end with '/'.
                    // Note that entries for directories themselves are optional.
                    // An entry's fileName implicitly requires its parent directories to exist.
                    mkdirp(entry.fileName, output, function () {
                        if (arguments[0]) throw arguments[0];
                        zipfile.readEntry();
                    });

                } else {
                    zipfile.openReadStream(entry, function (err, readStream) {
                        if (err) throw err;

                        const filter = new Transform();
                        filter._transform = function (chunk, _encoding, cb) {
                            cb(null, chunk);
                        };
                        filter._flush = function (cb) {
                            cb();
                            zipfile.readEntry();
                        };
                        const writeStream = createWriteStream(join(output, entry.fileName))
                        readStream?.pipe(filter).pipe(writeStream);
                    });
                }
            });
        });
    })
}

function getfname(oldname: string): string {
    const [o, i] = oldname.split('/')
    if (o) return o
    return i
}

/**
 * 使用tar解压
 * @param output 
 * @param input 
 */
export function tarany(output: string, input: string, name: Name) {
    if (!existsSync(input)) {
        console.log('没有找到文件')
        return Promise.resolve(false)
    }
    return new Promise<boolean>(res => {
        let d = ''
        const tar = spawn('tar', ['-xvf', input, '-C', output])
        tar.stdout.on('close', () => {
            if (d) {
                const oldname = getfname(d)
                rename(join(output, oldname), join(output, name), function (err) {
                    if (err) {
                        res(false)
                    } else {
                        res(true)
                    }
                })
            }
        })
        tar.stdout.on('data', (data) => {
            if (!d) d = data.toString()
        })
        tar.stdout.on('error', (err) => {
            console.log(err)
            res(false)
        })
    })
}


type SUFFIX = 'tar' | 'zip' | false

export function getSuffix(name: string): SUFFIX {
    if (name.includes('.zip')) return 'zip'
    if (name.includes('.tar')) return 'tar'
    return false
}


export async function installArduino() {
    try {
        const p = url('arduino', platform(), arch())
        if (!p) return false
        const down = await Download(p)
        if (!down) return false
        const arduino = join(config.basePath, '/arduino/')
        mkdirSync(arduino, { recursive: true })
        if (!getSuffix(down)) return
        if (getSuffix(down) === 'tar') return tarany(down, arduino, 'arduino')
        if (getSuffix(down) === 'zip') return Install(down, arduino, 'arduino')
        return false
    } catch (e) {
        return false
    }
}

function mkdirp(dir: string, output: string, cb: () => void) {
    if (dir === ".") {
        return cb();
    }
    stat(join(output, dir), (err) => {
        if (err == null) {
            return cb(); // already exists
        }
        var parent = dirname(dir);
        mkdirp(parent, output, () => {
            mkdir(join(output, dir), cb);
        });
    });
}
